package com.xunyin.netty;

import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author jthong
 * @Description 实体缓存对象
 * @Version 1.0.0
 * @Date 2019/12/26 16:18
 */
public abstract class EntityCachedObject<T, PK extends Serializable> {
    private static final Logger LOGGER = LoggerFactory.getLogger(EntityCachedObject.class);
    private static final ConcurrentLinkedHashMap.Builder<String, ReentrantReadWriteLock.WriteLock> BUILDER = new ConcurrentLinkedHashMap.Builder<>();
    private static final ConcurrentLinkedHashMap<String, ReentrantReadWriteLock.WriteLock> LOCK_MAP = BUILDER.maximumWeightedCapacity(100).weigher(Weighers.singleton()).build();

    private static final ConcurrentLinkedHashMap.Builder<String, Cache> ENTITY_CACHE_BUILDER = new ConcurrentLinkedHashMap.Builder<>();
    private static final ConcurrentLinkedHashMap<String, Cache> ENTITY_CACHE_MAP = ENTITY_CACHE_BUILDER.maximumWeightedCapacity(20000).weigher(Weighers.singleton()).build();

    private static final ConcurrentLinkedHashMap.Builder<String, ConcurrentHashMap<String, Cache>> COMMON_CACHE_BUILDER = new ConcurrentLinkedHashMap.Builder<>();
    private static final ConcurrentLinkedHashMap<String, ConcurrentHashMap<String, Cache>> COMMON_CACHE_MAP = COMMON_CACHE_BUILDER.maximumWeightedCapacity(5000).weigher(Weighers.singleton()).build();

    /**
     * 取得当前组的 HashKey
     */
    protected abstract String getHashKey();

    /**
     * 从数据库获得数据对象
     *
     * @param id 获得数据ID
     * @return Object 数据库的缓存对象
     */
    protected abstract T getEntityFromDB(PK id);

    protected List<T> getEntityFromIdList(List<PK> idList, Class<T> clazz) {
        List<T> entityList = new ArrayList<>(idList == null ? 0 : idList.size());
        if (idList != null && !idList.isEmpty()) {
            for (PK id : idList) {
                T entityFromCache = this.get(id, clazz);
                if (entityFromCache != null) {
                    entityList.add(entityFromCache);
                }
            }
        }
        return entityList;
    }

    /**
     * 获得通用缓存
     *
     * @param subKey
     * @return
     */
    protected Object getFromCommonCache(String subKey) {
        Cache cache = null;
        String hashKey = this.getHashKey();
        ConcurrentHashMap<String, Cache> cacheMap = COMMON_CACHE_MAP.get(hashKey);
        if (cacheMap != null && !cacheMap.isEmpty()) {
            cache = cacheMap.get(subKey);
        }
        return cache == null || cache.isTimeout() ? null : cache.getValue();
    }

    /**
     * 把存储信息加到缓存中
     *
     * @param subKey
     * @param value
     */
    protected void putToCommonHashCache(String subKey, Object value) {
        String hashKey = this.getHashKey();
        Map<String, Cache> cacheMap = COMMON_CACHE_MAP.get(hashKey);
        if (cacheMap == null) {
            COMMON_CACHE_MAP.put(hashKey, new ConcurrentHashMap<>());
            cacheMap = COMMON_CACHE_MAP.get(hashKey);
        }
        cacheMap.put(subKey, Cache.valueOf(value));
    }

    /**
     * 移除主KEY
     *
     * @param hashkey 主KEY
     */
    protected void removeFromCommonHashCache(String hashkey) {
        COMMON_CACHE_MAP.remove(hashkey);
    }

    /**
     * 移除SUBKEY
     *
     * @param hashKey 主KEY
     * @param subkey  子KEY
     */
    protected void removeFromCommonSubCache(String hashKey, String subkey) {
        Map<String, Cache> hashMap = COMMON_CACHE_MAP.get(hashKey);
        if (hashMap != null) {
            hashMap.remove(subkey);
        }
    }

    /**
     * 从缓存中移除实体对象
     *
     * @param id
     * @param clazz
     */
    protected void removeEntityFromCache(PK id, Class<T> clazz) {
        ENTITY_CACHE_MAP.remove(this.getEntityIdKey(id, clazz));
    }


    /**
     * 取得对象的读写锁
     *
     * @param clazz 类对象
     * @return WriteLock    写锁
     */
    private ReentrantReadWriteLock.WriteLock getWriteLock(Class<T> clazz) {
        String clazzNameKey = clazz.getName();
        ReentrantReadWriteLock.WriteLock writeLock = LOCK_MAP.get(clazzNameKey);
        if (writeLock == null) {
            LOCK_MAP.putIfAbsent(clazzNameKey, new ReentrantReadWriteLock().writeLock());
            writeLock = LOCK_MAP.get(clazzNameKey);
        }
        return writeLock;
    }

    /**
     * 取得实体IDKEY
     *
     * @param id
     * @param clazz
     * @param <T>
     * @return
     */
    private <T, PK> String getEntityIdKey(PK id, Class<T> clazz) {
        return new StringBuilder().append(clazz.getName()).append("_").append(id).toString();
    }

    /**
     * 从缓存获得实体对象
     *
     * @param id
     * @param clazz
     * @return
     */
    protected T get(PK id, Class<T> clazz) {
        String entityIdKey = this.getEntityIdKey(id, clazz);
        Cache cachedVO = ENTITY_CACHE_MAP.get(entityIdKey);
        if (cachedVO != null && !cachedVO.isTimeout()) {
            return (T) cachedVO.getValue();
        }

        ReentrantReadWriteLock.WriteLock writeLock = this.getWriteLock(clazz);
        try {
            writeLock.lock();
            cachedVO = ENTITY_CACHE_MAP.get(entityIdKey);
            if (cachedVO == null || cachedVO.isTimeout()) {
                Object entityFromDB = this.getEntityFromDB(id);
                ENTITY_CACHE_MAP.put(entityIdKey, Cache.valueOf(entityFromDB));
                return (T) entityFromDB;
            }
        } catch (Exception e) {
            LOGGER.error("{}", e);
        } finally {
            writeLock.unlock();
        }
        return null;
    }
}